#include"Common.hpp"
#include"InetAddr.hpp"
#ifndef __UDO_SERVER_HOO__
#define __UDO_SERVER_HOO__

#include<iostream>
#include<string>
#include<memory>
#include<functional>
#include<unistd.h>
#include<strings.h>
#include<sys/types.h>
#include<sys/socket.h>
//下面两个头文件是针对于通信是的结构体的创建的，是sockaddr_in还是sockaddr_un网络还是本地
#include<netinet/in.h>
#include<arpa/inet.h>

using func_t=std::function<std::string(const std::string&)>;//回调方法


const static int gsockfd=-1;//设置一个全局的sockfd去进行初始化。
const static std::string gdefaultip="127.0.0.1";
//default是默认的意思，表示本地机子的ip地址。固定写法每个机器都是这个。
const static uint16_t gdefaultport=8080;
//默认设置8080，1024以内的端口号不行，原则上(2^16)-1怎么设置端口号都行。
//但是0-1023是服务端绑定了端口，被其他用了。尽量不要冲突，所以这里选择是8080.

class UdpServer
{
public:
    UdpServer(func_t func,uint16_t port=gdefaultport)
    :_sockfd(gsockfd)
    ,_addr(port)
    // ,_port(port)
    // ,_ip(ip)
    ,_isrunning(false)
    ,_func(func)//把功能带出去
    {}
    void InitServer()
    {
        //1.先创建套接字，只是创建的文件

        _sockfd=::socket(AF_INET,SOCK_DGRAM,0);//这里的::是访问全局的socket的意思,这里的AF_INET是告诉OS这里是网络的接口
        
        if(_sockfd<0)//失败了
        {
            std::cout<<"创建套接字 error"<<std::endl;
            //直接死掉，并输出日志信息TODO
            Die(SOCKET_ERR);
        }
        //2.填充网络，用bind去绑定通信方式。
        // struct sockaddr_in local;
        // bzero(&local,sizeof(local));//初始清零结构体。
        // local.sin_family=AF_INET;//这里是为了填充网络信息，填充这个结构体的。
        // local.sin_port=htons(_port);//这里填充的就是端口号
        //除了收消息还是要回消息的。需要发送给对方的，即需要发送到网络中！！
        //目的IP和目的端口号，以及原IP和原端口号是必要的。
        //所以这里port是要转大小端的。用到
        //local.sin_addr.s_addr=::inet_addr(_ip.c_str());//这里c语言中结构体是不能整体初始化，只能对整体赋值
        //1.string ip->4字节的IP  2.4字节IP转网络IP。
        // local.sin_addr.s_addr=INADDR_ANY;
        //对struct sockaddr_in再进行封装后上面的步骤就省去了。
        //2.1用bind3
        //封装后
        int n=::bind(_sockfd, _addr.NetAddr(),_addr.NetAddrLen());//在bind时才是把socket信息放到内核中。这是一个固定的用法。
        // int n=::bind(_sockfd, CONV(&local),sizeof(local));//在bind时才是把socket信息放到内核中。这是一个固定的用法。
        //一样的要判断是否不操作失败
        if(n<0)
        {
            std::cout<<"bind error"<<std::endl;
            Die(BIND_ERR);
        }
        std::cout<<"bind 成功"<<std::endl;
    }

    void Start()
    {
        _isrunning=true;
        while(true)
        {
            char inbuffer[1024];//string
            struct sockaddr_in peer;
            socklen_t len=sizeof(peer);//必须设定的

            ssize_t n=::recvfrom(_sockfd,inbuffer,sizeof(inbuffer)-1,0,const_cast<struct sockaddr*>(CONV(&peer)),&len);
            if(n>0)
            {
                //1.消息内容2.谁发的内容
                // 1.1没有封装的版本
                // uint16_t clientport=::ntohs(peer.sin_port);
                // std::string clientip=::inet_ntoa(peer.sin_addr);
                // inbuffer[n]=0;
                // std::string clientinfo=clientip+":"+std::to_string(clientport)+" # "+inbuffer;
                // std::cout<<clientinfo<<std::endl;//发信息回去用sendto
                //1.2封装后的版本
                // InetAddr cli(peer);
                // inbuffer[n]=0;
                // std::string clientinfo=cli.Ip()+":"+std::to_string(cli.Port())+" # "+inbuffer;
                // std::cout<<clientinfo<<std::endl;
                // std::string echo_string="echo# "; 
                // echo_string+=inbuffer;
                //翻译功能，把英文单词转换成为中文
                inbuffer[n]=0;
                std::string result=_func(inbuffer);
                ::sendto(_sockfd,result.c_str(),result.size(),0,const_cast<struct sockaddr*>(CONV(&peer)),len);
                 
            }

        }
        _isrunning=false;

    }
    ~UdpServer()//一般服务器是不会停止的，最多是关闭文件描述符。
    {
        if(_sockfd>gsockfd)
            ::close(_sockfd);
    }
private:
    int _sockfd;
    // uint16_t _port;//服务器未来的端口号。//无符号16位整数
    //std::string _ip;//服务器所对应的IP。这是服务器的IP字符串风格的IP，而在网络通信中IP地址是32位也就是4字节的IP风格。
    InetAddr _addr;
    bool _isrunning;//表示服务器开始运行。

    //业务，回调方法
    func_t _func;
};

#endif